/**
 *    @file       sockbase.cpp
 *
 *    @brief      
 *    @details
 *
 *    @version    1.0
 *    @date       12/21/2012 04:21:41 PM
 *
 *    @author     Theophilus (), wangtf418@gmail.com
 */
#include <netinet/tcp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include "sockbase.h"
#include "domainparse.h"
#include "toolkit.h"
#include "endianness_trans.h"

//class sockbase////////////////////////////////////////////////
sockbase::sockbase()
	: m_socket(INVALID_SOCKET)
{
	memset(&m_addr, 0, sizeof(m_addr));
}

sockbase::~sockbase()
{

}

bf_int_t sockbase::socket(bf_int_t socktype)
{
	bf_int_t ret = close();
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	bf_int_t sock = ::socket(AF_INET, socktype, 0);
	if (sock < 0)
	{
		return BF_ERROR;
	}
	m_socket = sock;
	return BF_OK;
}

bf_int_t sockbase::close()
{
	if (m_socket != INVALID_SOCKET)
	{
		bf_int_t ret = ::close(m_socket);
		m_socket = INVALID_SOCKET;
		memset(&m_addr, 0, sizeof(m_addr));
		if (ret != 0)
		{
			return BF_ERROR;
		}
	}
	return BF_OK;
}

bf_int_t sockbase::bind(const std::string &host, bf_ushort_t port)
{
	if (host.empty())
	{
		return bind(INADDR_ANY, port);
	}

	bf_uint32_t inaddr;
	bf_int_t ret = toolkit::is_ipstring(host);
	if (ret != BF_OK)
	{
		ret = domainparse::parse_domain(host, inaddr);
	}
	else
	{
		ret = toolkit::str2inaddr(host, inaddr);
	}

	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	
	bf_uint32_t ip = bf_ntohl(inaddr);
	return bind(ip, port);
}

bf_int_t sockbase::bind(bf_uint_t hostip, bf_ushort_t port)
{
	m_addr.sin_family = AF_INET;
	m_addr.sin_addr.s_addr = bf_htonl(hostip);
	m_addr.sin_port = bf_htons(port);
	bf_int_t ret = ::bind(m_socket, (struct sockaddr *)&m_addr, sizeof(m_addr));
	if (ret != 0)
	{
		return BF_ERROR;
	}
	return BF_OK;
}

bf_int_t sockbase::listen(bf_int_t backlog /* = 1024 */)
{
	bf_int_t ret = ::listen(m_socket, backlog);
	if (ret != 0)
	{
		return BF_ERROR;
	}
	return BF_OK;
}

bf_int_t sockbase::connect(const std::string &host, bf_ushort_t port)
{
	if (host.empty())
	{
		return BF_ERROR;
	}

	bf_uint32_t inaddr;
	bf_int_t ret = toolkit::is_ipstring(host);
	if (ret != BF_OK)
	{
		ret = domainparse::parse_domain(host, inaddr);
	}
	else
	{
		ret = toolkit::str2inaddr(host, inaddr);
	}

	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	
	bf_uint32_t ip = bf_ntohl(inaddr);
	return connect(ip, port);
}

bf_int_t sockbase::connect(bf_uint_t hostip, bf_ushort_t port)
{
	m_addr.sin_family = AF_INET;
	m_addr.sin_addr.s_addr = bf_htonl(hostip);
	m_addr.sin_port = bf_htons(port);
	bf_int_t ret = ::connect(m_socket, (struct sockaddr *)&m_addr, sizeof(m_addr));
	if (ret != 0)
	{
		return BF_ERROR;
	}
	return BF_OK;
}

bf_int_t sockbase::accept(struct sockaddr_in &addr)
{
	socklen_t socklen = sizeof(addr);
	return ::accept(m_socket, (struct sockaddr *)&addr, &socklen);
}

bf_int_t sockbase::recv(void *recv_buf, bf_size_t buf_len, bf_int_t flag /* = 0 */)
{
	return ::recv(m_socket, recv_buf, buf_len, flag);	//return  the number of bytes received, or -1 if an error occurred
}

bf_int_t sockbase::send(const void *send_buf, bf_size_t buf_len, bf_int_t flag /* = 0 */)
{
	return ::send(m_socket, send_buf, buf_len, flag);	//return  the number of bytes sent, or -1 if an error occurred
}

bf_int_t sockbase::recvfrom(void *recv_buf, bf_size_t buf_len, struct sockaddr_in &addr, bf_int_t flag /* = 0 */)
{
	socklen_t socklen = sizeof(addr);
	return ::recvfrom(m_socket, recv_buf, buf_len, flag, (struct sockaddr *)&addr, &socklen);		//like recv
}

bf_int_t sockbase::sendto(const void *send_buf, bf_size_t buf_len, const std::string &host, bf_ushort_t port, bf_int_t flag /* = 0 */)
{
	if (host.empty())
	{
		return BF_NEGATIVE_ONE;
	}

	bf_uint32_t inaddr;
	bf_int_t ret = toolkit::is_ipstring(host);
	if (ret != BF_OK)
	{
		ret = domainparse::parse_domain(host, inaddr);
	}
	else
	{
		ret = toolkit::str2inaddr(host, inaddr);
	}

	if (ret != BF_OK)
	{
		return BF_NEGATIVE_ONE;
	}
	
	bf_uint32_t ip = bf_ntohl(inaddr);
	return sendto(send_buf, buf_len, ip, port, flag);
}

bf_int_t sockbase::sendto(const void *send_buf, bf_size_t buf_len, bf_uint_t hostip, bf_ushort_t port, bf_int_t flag /* = 0 */)
{
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = bf_htonl(hostip);
	addr.sin_port = bf_htons(port);
	return sendto(send_buf, buf_len, addr, flag);
}

bf_int_t sockbase::sendto(const void *send_buf, bf_size_t buf_len, const struct sockaddr_in &addr, bf_int_t flag /* = 0 */)
{
	return ::sendto(m_socket, send_buf, buf_len, flag, (const struct sockaddr *)&addr, sizeof(addr));	//like send
}

bf_int_t sockbase::setsockopt(bf_int_t level, bf_int_t optname, const void *optval, bf_size_t optlen)
{
	bf_int_t ret = ::setsockopt(m_socket, level, optname, optval, optlen);
	if (ret != 0)
	{
		return BF_ERROR;
	}
	return BF_OK;
}

bf_int_t sockbase::getsockopt(bf_int_t level, bf_int_t optname, void *optval, bf_size_t optlen)
{
	socklen_t socklen = optlen;
	bf_int_t ret = ::getsockopt(m_socket, level, optname, optval, &socklen);
	if (ret != 0)
	{
		return BF_ERROR;
	}
	return BF_ERROR;
}

bf_int_t sockbase::get_socket_error()
{
	bf_int_t sock_err = 0;
	bf_int_t ret = getsockopt(SOL_SOCKET, SO_ERROR, &sock_err, sizeof(sock_err));
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	return sock_err;
}

bf_int_t sockbase::set_nonblock_opt(bf_bool_t nonblock_flag /* = true */)
{
	bf_int_t opt = fcntl(m_socket, F_GETFL);
	if (opt == -1)
	{
		return BF_ERROR;
	}
	if (nonblock_flag)
	{
		opt = opt | O_NONBLOCK;
		opt = opt | O_NDELAY;
	}
	else
	{
		opt = opt & (~O_NONBLOCK);
		opt = opt & (~O_NDELAY);
	}
	bf_int_t ret = fcntl(m_socket, F_SETFL, opt);
	if (ret == -1)
	{
		return BF_ERROR;
	}
	return BF_OK;
}

bf_int_t sockbase::set_tcpnodelay_opt(bf_bool_t nodelay_flag /* = true */)
{
	bf_int_t nodelay_opt = 0;
	if (nodelay_flag)
	{
		nodelay_opt = 1;
	}
	return setsockopt(SOL_TCP, TCP_NODELAY, &nodelay_opt, sizeof(nodelay_opt));
}

bf_int_t sockbase::set_reuse_opt(bf_bool_t reuse_flag /* = true */)
{
	bf_int_t reuse_opt = 0;
	if (reuse_flag)
	{
		reuse_opt = 1;
	}
	return setsockopt(SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(reuse_opt));

}

bf_int_t sockbase::set_recvbuf_opt(bf_int_t bufsize)
{
	return setsockopt(SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));
}

bf_int_t sockbase::set_sendbuf_opt(bf_int_t bufsize)
{
	return setsockopt(SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
}

//csocket////////////////////////////////////////////////
atomic_uint32 csocket::m_socket_num;

csocket::csocket() : m_hostip(0), m_port(0)
{
	m_socket_num.add_return(1);
}

csocket::csocket(bf_uint_t hostip, bf_ushort_t port)
	: m_hostip(hostip), m_port(port)
{
	m_socket_num.add_return(1);
}

csocket::csocket(bf_int_t sock, const struct sockaddr_in &addr)
	: m_hostip(0), m_port(0)
{
	m_socket = sock;
	memcpy(&m_addr, &addr, sizeof(m_addr));
	m_socket_num.add_return(1);
}

csocket::~csocket()
{
	close();
	m_socket_num.sub_return(1);
}

//udpclient_socket//////////////////////////////////////
udpclient_socket::udpclient_socket(const std::string &srvhost, bf_ushort_t srvport, const std::string &host /* = "0.0.0.0" */, bf_ushort_t port /* = 0 */)
	: m_host(host), m_port(port), m_srvhost(srvhost), m_srvport(srvport)
{
	memset(&m_srv_addr, 0, sizeof(m_srv_addr));
}

bf_int_t udpclient_socket::init()
{
	bf_int_t ret = socket(SOCK_DGRAM);
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}

	set_reuse_opt(true);
	bf_int_t socket_buf_len = 8388608;	//8MB
	ret = set_recvbuf_opt(socket_buf_len);
	ret = set_sendbuf_opt(socket_buf_len);
	set_nonblock_opt(true);

	ret = bind(m_host, m_port);
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}

	bf_uint_t srvhostaddr = toolkit::str2inaddr(m_srvhost);
	if (srvhostaddr == INADDR_NONE)
	{
		return BF_ERROR;
	}
	m_srv_addr.sin_family = AF_INET;
	m_srv_addr.sin_addr.s_addr = srvhostaddr;
	m_srv_addr.sin_port = bf_htons(m_srvport);

	ret = m_epoll.init(1, 100);
	if (ret != BF_OK)
	{
		return BF_ERROR;
	}
	m_epoll.add(m_socket, 0, NULL);
	return BF_OK;
}

bf_int_t udpclient_socket::sendto(const void *send_buf, bf_size_t buf_len, bf_time_t timeout)
{
	if (!send_buf || buf_len == 0)
	{
		return BF_NEGATIVE_ONE;
	}
	bf_int_t sendlen = sendto(send_buf, buf_len, m_srv_addr);
	if (sendlen > 0)
	{
		return sendlen;
	}

	m_epoll.modify(m_socket, BF_EPOLL_OUT, NULL);
	bf_int_t ret = BF_NEGATIVE_ONE;
	bf_int_t waitret = m_epoll.wait(timeout);
	if (waitret > 0)
	{
		bf_epoll_event_t *epoll_event = m_epoll.get_next_avail();
		if ((epoll_event->events & BF_EPOLL_ERR) == BF_EPOLL_ERR || (epoll_event->events & BF_EPOLL_HUP) == BF_EPOLL_HUP)
		{
			m_epoll.modify(m_socket, 0, NULL);
			return BF_NEGATIVE_ONE;
		}

		if ((epoll_event->events & BF_EPOLL_OUT) == BF_EPOLL_OUT)
		{
			if ((ret = sendto(send_buf, buf_len, m_srv_addr)) <= 0)
			{
				ret = BF_NEGATIVE_ONE;
			}
		}
	}
	m_epoll.modify(m_socket, 0, NULL);
	return ret;
}

bf_int_t udpclient_socket::recvfrom(void *recv_buf, bf_size_t buf_len, bf_time_t timeout)
{
	if (!recv_buf || buf_len == 0)
	{
		return BF_NEGATIVE_ONE;
	}
	m_epoll.modify(m_socket, BF_EPOLL_IN, NULL);

	bf_int_t ret = BF_NEGATIVE_ONE;
	bf_int_t waitret = m_epoll.wait(timeout);
	if (waitret > 0)
	{
		bf_epoll_event_t *epoll_event = m_epoll.get_next_avail();
		if ((epoll_event->events & BF_EPOLL_ERR) == BF_EPOLL_ERR || (epoll_event->events & BF_EPOLL_HUP) == BF_EPOLL_HUP)
		{
			m_epoll.modify(m_socket, 0, NULL);
			return BF_NEGATIVE_ONE;
		}

		if ((epoll_event->events & BF_EPOLL_IN) == BF_EPOLL_IN)
		{
			struct sockaddr_in addr;
			if ((ret = recvfrom(recv_buf, buf_len, addr)) <= 0)
			{
				ret = BF_NEGATIVE_ONE;
			}
		}
	}
	m_epoll.modify(m_socket, 0, NULL);
	return ret;
}

